/**
 * 
 */
package org.rhq.plugins.tools.httpcheck;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Set;
import java.util.prefs.Preferences;

import org.apache.log4j.Logger;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.ConfigurationTemplate;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.configuration.ConfigurationFacet;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;

/**
 * Each instance represents one http server.
 * 
 * Metrics are:
 *   Response_Time
 *   Response_Code
 * 
 * Resource Configurations are:
 * 	timeout
 *  administrator_email
 *  email_subject
 *  email_content
 * 
 * @author lgao
 *
 */
public class HttpHostResourceComponent implements ResourceComponent, HttpCheckerConstants, MeasurementFacet, ConfigurationFacet{

	private static final Logger LOG = Logger.getLogger(HttpHostResourceComponent.class);
	
	/*
	 * FIXME: Preferences differ by the host:port pair, if the host:port is changed after the preferences is created. it may confuse the GUI.
	 * 
	 */
	private Preferences store = Preferences.userNodeForPackage(HttpHostResourceComponent.class);
	
	private ResourceContext context;
	
	private long responseTime;
	
	private int  responseCode;
	
	public AvailabilityType getAvailability() {
			HttpURLConnection conn = null;
			try {
				Configuration config = context.getPluginConfiguration();
				String host = config.getSimpleValue(PLUGIN_CONFIG_HOST_KEY, null);
				Integer port = Integer.valueOf(config.getSimpleValue(PLUGIN_CONFIG_PORT_KEY, "80"));
				
				Configuration resConfig = loadResourceConfiguration();
				int timeout = Integer.valueOf(resConfig.getSimpleValue(RES_CONFIG_TIMEOUT, "0"));
				long startTime = System.currentTimeMillis();
				URL url = new URL("http://" + host + ":" + port);
				LOG.info("URL is: " + url.toString());
				conn = (HttpURLConnection)url.openConnection();
				conn.setConnectTimeout(timeout);
				conn.connect();
				long endTime = System.currentTimeMillis();
				responseTime = endTime - startTime;
				responseCode = conn.getResponseCode();
				return AvailabilityType.UP;
			} catch (MalformedURLException e) {
				LOG.warn(e);
				return AvailabilityType.DOWN;
			} catch (IOException e) {
				LOG.warn(e);
				sendNotification(null);
				return AvailabilityType.DOWN;
			} catch (Exception e) {
				LOG.warn(e);
				sendNotification(null);
				return AvailabilityType.DOWN;
			}finally{
				if(null != conn){
					conn.disconnect();
				}
			}
	}
	
	public void start(ResourceContext _context)
			throws InvalidPluginConfigurationException, Exception {
		this.context = _context;
	}
	
	private Preferences getStoreRootPref(){
		Configuration config = context.getPluginConfiguration();
		String host = config.getSimpleValue(PLUGIN_CONFIG_HOST_KEY, null);
		Integer port = Integer.valueOf(config.getSimpleValue(PLUGIN_CONFIG_PORT_KEY, "80"));
		String rootPath = RES_CONFIG_GRP_DETAIL + "/" + host + "_" + port;
		return store.node(rootPath);
	}
	

	public void stop() {
		this.context = null;
	}

	
	public void getValues(MeasurementReport report,
			Set<MeasurementScheduleRequest> metrics) throws Exception {
		
		Configuration resConfig = loadResourceConfiguration();
		int timeout = Integer.valueOf(resConfig.getSimpleValue(RES_CONFIG_TIMEOUT, "0"));
		if(this.responseTime > timeout){
			sendNotification(null);
		}
		
		for(MeasurementScheduleRequest request: metrics){
			if(request.getName().equals(METRICS_RESPONSE_TIME)){
				report.addData(new MeasurementDataNumeric(request, Double.valueOf(responseTime)));
			}else if(request.getName().equals(METRICS_RESPONSE_CODE)){
				report.addData(new MeasurementDataNumeric(request, Double.valueOf(responseCode)));
			}
		}
	}

	// if response time is larger than configured one, or any exception.
	/**
	 * If path is not null, means the path will be appended to the mail content
	 */
	protected void sendNotification(String path) {
		HttpCheckResourceComponent checkResComp = (HttpCheckResourceComponent)context.getParentResourceComponent();
		try {
			Configuration mailResConfig = checkResComp.loadResourceConfiguration();
			Boolean openSmtp = Boolean.valueOf(mailResConfig.getSimpleValue(RES_CONFIG_OPEN_SMTP, "false"));
			if(openSmtp){
				LOG.info("Start sending mail.");
				Configuration config = context.getPluginConfiguration();
				String host = config.getSimpleValue(PLUGIN_CONFIG_HOST_KEY, null);
				Integer port = Integer.valueOf(config.getSimpleValue(PLUGIN_CONFIG_PORT_KEY, "80"));
				String wholePath = "http://" + host + ":" + port + "/" + path;
				MailSender sender = new MailSender(mailResConfig, loadResourceConfiguration(), wholePath);
				sender.start();
			}
		} catch (Exception e) {
			LOG.warn(e);
		}
	}
	
	protected Configuration loadPluginConfiguration(){
		return this.context.getPluginConfiguration();
	}

	public Configuration loadResourceConfiguration() throws Exception {
		Preferences grpPref = getStoreRootPref();
		Configuration plugConfig = context.getPluginConfiguration();
		String host = plugConfig.getSimpleValue(PLUGIN_CONFIG_HOST_KEY, null);
		Integer port = Integer.valueOf(plugConfig.getSimpleValue(PLUGIN_CONFIG_PORT_KEY, "80"));
		
		Configuration config = new Configuration();
		// timeout
		Integer timeout = grpPref.getInt(RES_CONFIG_TIMEOUT, 5000);
		config.put(new PropertySimple(RES_CONFIG_TIMEOUT, timeout));
		
		// administrator`s email
		String emailTo = grpPref.get(RES_CONFIG_ADMIN_EMAIL, "");
		config.put(new PropertySimple(RES_CONFIG_ADMIN_EMAIL, emailTo));
		
		ConfigurationDefinition configDef = context.getResourceType().getResourceConfigurationDefinition();
		
		ConfigurationTemplate template = configDef.getDefaultTemplate();
		Configuration defConfig = template.getConfiguration();
		
		// email subject, default use the definition
		String defaultSub = defConfig.getSimple(RES_CONFIG_SUBJECT).getStringValue();
		// replace $host, $port in the subject
		defaultSub = defaultSub.replaceAll("$host", host);
		defaultSub = defaultSub.replaceAll("$port", String.valueOf(port));
		
		String subject = grpPref.get(RES_CONFIG_SUBJECT, defaultSub);
		subject = subject.replaceAll("$host", host);
		subject = subject.replaceAll("$port", String.valueOf(port));
		config.put(new PropertySimple(RES_CONFIG_SUBJECT, subject));
		
		//email content, default use the definition
		String dftContent = defConfig.getSimple(RES_CONFIG_CONTENT).getStringValue();
		// replace $host, $port $timeout
		dftContent = dftContent.replaceAll("$host", host);
		dftContent = dftContent.replaceAll("$port", String.valueOf(port));
		dftContent = dftContent.replaceAll("$timeout", String.valueOf(timeout));
		
		String content = grpPref.get(RES_CONFIG_CONTENT, dftContent);
		content = content.replaceAll("$host", host);
		content = content.replaceAll("$port", String.valueOf(port));
		content = content.replaceAll("$timeout", String.valueOf(timeout));
		
		config.put(new PropertySimple(RES_CONFIG_CONTENT, content));
		
		return config;
	}

	public void updateResourceConfiguration(ConfigurationUpdateReport report) {
		Preferences grpPref = getStoreRootPref();
		Configuration config = report.getConfiguration();

		// timeout
		grpPref.putInt(RES_CONFIG_TIMEOUT, Integer.valueOf(config.getSimpleValue(RES_CONFIG_TIMEOUT, "5000")));
		
		// email
		grpPref.put(RES_CONFIG_ADMIN_EMAIL, config.getSimpleValue(RES_CONFIG_ADMIN_EMAIL, ""));
		
		// email subject
		grpPref.put(RES_CONFIG_SUBJECT, config.getSimpleValue(RES_CONFIG_SUBJECT, ""));
		
		// email content
		grpPref.put(RES_CONFIG_CONTENT, config.getSimpleValue(RES_CONFIG_CONTENT, ""));
		
		report.setStatus(ConfigurationUpdateStatus.SUCCESS);
	}

}
